package com.showsu.editor.controller.biz;

import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.diagnosis.DiagnosisUtils;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.GoodsDetail;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.showsu.editor.config.AlipayProperties;
import com.showsu.editor.controller.BaseController;
import com.showsu.editor.entity.doo.User;
import com.showsu.editor.entity.doo.VipGoods;
import com.showsu.editor.entity.doo.VipOrder;
import com.showsu.editor.entity.dto.AlipayPagePayRequestDTO;
import com.showsu.editor.entity.dto.VipOrderRequestDTO;
import com.showsu.editor.entity.po.TblUser;
import com.showsu.editor.enums.PayStatusEnum;
import com.showsu.editor.enums.ResultCode;
import com.showsu.editor.enums.UserVipEnum;
import com.showsu.editor.enums.VipLevelEnum;
import com.showsu.editor.service.IUserPrivilegeService;
import com.showsu.editor.service.IVipGoodsService;
import com.showsu.editor.service.IVipOrderService;
import com.showsu.editor.utils.BsResult;
import com.showsu.editor.utils.Constant;
import com.showsu.editor.utils.DateUtil;
import com.showsu.editor.utils.StringUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * @Author: William
 * @Description:
 * @Date: Created in 3:45 下午 2024/9/26
 * @Modified By:
 */
@RestController
@RequestMapping("/api/pay")
@Api(value = "PayController", tags = "支付", description = "支付相关接口", position = 3)
@Slf4j
public class PayController extends BaseController {

    private static final String alipayNotifyUrl = "https://creator.datashows.cn/api/pay/alipay/notify/order";

    @Autowired
    private AlipayProperties alipayProperties;

    @Autowired
    private IVipGoodsService vipGoodsService;

    @Autowired
    private IVipOrderService vipOrderService;

    @Autowired
    private IUserPrivilegeService userPrivilegeService;

    @ApiOperation(value = "查询订单状态")
    @PostMapping("/order/status")
    @ResponseBody
    public BsResult orderQuery(@RequestBody VipOrderRequestDTO requestDTO) {
        if (requestDTO == null || StringUtil.isEmpty(requestDTO.getOutTradeNo())) {
            return BsResult.error(ResultCode.PARAM_MISS);
        }
        VipOrder vipOrder = vipOrderService.queryByOrderTradeNo(requestDTO.getOutTradeNo());
        if (vipOrder == null
                || vipOrder.getUserId() == null
                || ! vipOrder.getUserId().equals(getSession().getUserId())) {
            return BsResult.error(ResultCode.PARAM_VALID_ERROR);
        }
        Map<String, Object> result = new HashMap<>();
        result.put("status", vipOrder.getStatus());
        return BsResult.success(result);
    }

    /**
     * https://opendocs.alipay.com/open/270/105899?pathHash=d57664bf
     */
    @ApiOperation(value = "支付宝网页支付下单")
    @PostMapping("/alipay/pagePay")
    @ResponseBody
    public BsResult alipayPagePay(@RequestBody AlipayPagePayRequestDTO requestDTO) {
        if (requestDTO == null || StringUtil.isEmpty(requestDTO.getVipLevel())) {
            return BsResult.error(ResultCode.PARAM_VALID_ERROR);
        }
        VipGoods vipGoods = vipGoodsService.queryByVipLevel(requestDTO.getVipLevel());
        if (vipGoods == null) {
            return BsResult.error(ResultCode.PARAM_VALID_ERROR);
        }
        // 创建订单
        VipOrder vipOrder = vipOrderService.save(getSession().getUserId(), vipGoods, getClientRequest());
        // 初始化SDK
        AlipayClient alipayClient = getAlipayClient();
        if (alipayClient == null) {
            return BsResult.error(ResultCode.INTERNAL_SERVER_ERROR);
        }
        // 构造请求参数以调用接口
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setNotifyUrl(alipayNotifyUrl);
        AlipayTradePagePayModel model = new AlipayTradePagePayModel();
        // 设置订单绝对超时时间，15分钟后
        model.setTimeExpire(DateUtil.formatDate(DateUtil.addMinutes(new Date(), 15), DateUtil.DATE_TIME_FORMAT));
        // 设置订单标题
        model.setSubject(Constant.creatorDatashows + vipOrder.getGoodName());
        // 设置产品码
        model.setProductCode("FAST_INSTANT_TRADE_PAY");
        // 设置PC扫码支付的方式
        model.setQrPayMode("4");
        // 设置商户自定义二维码宽度
        model.setQrcodeWidth(100L);
        // 设置请求后页面的集成方式
        model.setIntegrationType("PCWEB");
        // 设置订单包含的商品列表信息
        List<GoodsDetail> goodsDetail = new ArrayList<>();
        GoodsDetail goodsDetail0 = new GoodsDetail();
        goodsDetail0.setGoodsName(vipOrder.getGoodName());
        goodsDetail0.setQuantity(1L);
        goodsDetail0.setPrice(NumberUtil.div(new BigDecimal(vipOrder.getGoodPrice()), new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
        goodsDetail.add(goodsDetail0);
        model.setGoodsDetail(goodsDetail);
        // 设置商户订单号
        model.setOutTradeNo(vipOrder.getOrderTradeNo());
        // 设置订单总金额
        model.setTotalAmount(NumberUtil.div(new BigDecimal(vipOrder.getTotalFee()), new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
        request.setBizModel(model);
        //发起请求
        try {
            log.info("支付宝网页支付下单,request:{}", JSON.toJSONString(request));
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request, "GET");
            log.info("支付宝网页支付下单,response:{}", JSON.toJSONString(response));
            if (response.isSuccess()) {
                Map<String, Object> result = new HashMap<>();
                result.put("url", response.getBody());
                result.put("outTradeNo", vipOrder.getOrderTradeNo());
                return BsResult.success(result);
            } else {
                log.info("支付宝网页支付下单,失败,诊断链接:{}", DiagnosisUtils.getDiagnosisUrl(response));
                return BsResult.error(ResultCode.INITIATE_PAYMENT_FAIL);
            }
        } catch (Exception e) {
            log.error("支付宝网页支付下单,请求异常", e);
            return BsResult.error(ResultCode.INITIATE_PAYMENT_FAIL);
        }
    }

    @ApiOperation(value = "支付宝支付回调通知处理")
    @PostMapping("/alipay/notify/order")
    @Transactional
    public String alipayNotifyOrder(HttpServletRequest request, HttpServletResponse response) {
        //获取支付宝POST过来反馈信息
        boolean flag = false;
        Map<String, String> params = new HashMap<String, String>();
        try {
            Map requestParams = request.getParameterMap();
            for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
                String name = (String) iter.next();
                String[] values = (String[]) requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
                }
                //乱码解决，这段代码在出现乱码时使用。
                //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                params.put(name, valueStr);
            }
            //切记alipayPublicCertPath是支付宝公钥证书路径，请去open.alipay.com对应应用下载。
            try {
                flag = AlipaySignature.rsaCertCheckV1(params, alipayProperties.getAlipayCertPath(), "UTF-8", "RSA2");
            } catch (AlipayApiException e) {
                log.error("支付宝支付回调通知处理AlipaySignature异常", e);
            }
        } catch (Exception e) {
            log.error("支付宝支付回调通知处理异常", e);
        }
        log.info("支付宝支付回调参数:{}", JSON.toJSONString(params));
        if (flag) {
            // TODO 验签成功后
            /* 实际验证过程建议商户务必添加以下校验：
            1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
            2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
            3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email）
            4、验证app_id是否为该商户本身。
            */
            //开发者的 app_id。支付宝分配给开发者的应用 APPID。
            String app_id = params.get("app_id");
            if (! alipayProperties.getAppId().equals(app_id)) {
                log.error("支付宝支付回调通知处理异常：验签成功 但app_id 错误：{}，反馈信息：{}", app_id, JSON.toJSONString(params));
                return "fail";
            }
            //卖家支付宝用户号
            String seller_id = params.get("seller_id");
            if (! alipayProperties.getSellerId().equals(seller_id)) {
                log.error("支付宝支付回调通知处理异常：验签成功 但seller_id 错误：{}，反馈信息：{}", seller_id, JSON.toJSONString(params));
                return "fail";
            }
            //商户订单号
            String out_trade_no = params.get("out_trade_no");
            VipOrder vipOrder = vipOrderService.queryByOrderTradeNo(out_trade_no);
            if (vipOrder == null) {
                log.error("支付宝支付回调通知处理异常：验签成功 但是商户订单号不存在：{}，反馈信息：{}", out_trade_no, JSON.toJSONString(params));
                return "fail";
            }
            //订单金额。本次交易支付的订单金额，单位为人民币（元）。支持小数点后两位。
            String total_amount = params.get("total_amount");
            Long totalFee = NumberUtil.mul(new BigDecimal(total_amount), new BigDecimal(100)).longValue();
            if (! vipOrder.getTotalFee().equals(totalFee)) {
                log.error("支付宝支付回调通知处理异常：验签成功 但是该订单的实际金额错误：{}，订单创建时的金额为：{}，反馈信息：{}", total_amount, vipOrder.getTotalFee().toString(), JSON.toJSONString(params));
                return "fail";
            }
            //交易状态
            //WAIT_BUYER_PAY 交易创建，等待买家付款。
            //TRADE_CLOSED 未付款交易超时关闭，或支付完成后全额退款
            //TRADE_SUCCESS 交易支付成功。
            //TRADE_FINISHED 交易结束，不可退款。
            String trade_status = params.get("trade_status");
            if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {
                //TRADE_FINISHED注意：退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知
                //TRADE_SUCCESS注意：付款完成后，支付宝系统发送该交易状态通知
                //视为一样的状态 支付成功
                //判断该笔订单是否在商户网站中已经做过处理
                //如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                //如果有做过处理，不执行商户的业务程序
                //该订单已被处理不执行业务程序 ，如果不是支付成功 状态则执行 业务程序
                if (vipOrder.getStatus().equals(PayStatusEnum.Success.getCode())) {
                    return "success";
                }
                User user = userService.queryById(vipOrder.getUserId());
                if (user == null) {
                    log.error("支付宝支付回调通知处理异常：验签成功 但是用户不存在：{}，反馈信息：{}", vipOrder.getUserId(), JSON.toJSONString(params));
                    return "fail";
                }
                //支付宝交易号
                String trade_no = params.get("trade_no");
                //处理订单
                vipOrderService.updateVipOrder(vipOrder.getId(), trade_no, app_id, PayStatusEnum.Success.getCode(), JSON.toJSONString(params));
                //处理套餐信息
                VipGoods vipGoods = vipGoodsService.queryByVipLevel(vipOrder.getGoodId());
                //获取vip套餐枚举类
                VipLevelEnum vipLevelEnum = VipLevelEnum.getByLevel(vipGoods.getVipLevel());
                //资源包系列，不进行用户会员操作
                if (vipLevelEnum == null) {
                    log.info("支付宝支付回调，怎么会有这种情况，说明代码错误");
                    return "fail";
                }
                //产品的意思，这一版先不考了会员用户升级套餐带来的时间延长、差价等问题
                log.info("支付宝支付回调通知处理-处理会员信息");
                TblUser updateUser = new TblUser();
                updateUser.setId(user.getId());
                updateUser.setVipUser(UserVipEnum.VIP.getCode());
                Date vipEndTime = DateUtil.addDay(new Date(), vipGoods.getDays());
                updateUser.setVipEndTime(vipEndTime);
                updateUser.setVipLevelCode(vipGoods.getVipLevel());
                userService.updateById(updateUser);
                int num = userPrivilegeService.handleUserPrivilege(user.getId(), vipLevelEnum, vipEndTime, out_trade_no);
                log.info("支付宝支付回调通知处理-处理会员权益{}条", num);
            } else if (trade_status.equals("TRADE_CLOSED")) {
                //在订单状态为 待支付 支付中 支付失败的情况下 更新订单状态
                if (vipOrder.getStatus().equals(PayStatusEnum.Waiting_pay.getCode())
                        || vipOrder.getStatus().equals(PayStatusEnum.PayIng.getCode())
                        || vipOrder.getStatus().equals(PayStatusEnum.Fail.getCode())) {
                    vipOrderService.updateVipOrder(vipOrder.getId(), params.get("trade_no"), app_id, PayStatusEnum.Closed.getCode(), JSON.toJSONString(params));
                }
            } else if (trade_status.equals("WAIT_BUYER_PAY")) {
                //在订单状态为 待支付情况下 更新订单状态
                if (vipOrder.getStatus().equals(PayStatusEnum.Waiting_pay.getCode())) {
                    vipOrderService.updateVipOrder(vipOrder.getId(), params.get("trade_no"), app_id, PayStatusEnum.PayIng.getCode(), JSON.toJSONString(params));
                }
            }
            return "success";
        }
        log.info("支付宝支付回调，验签失败");
        return "fail";
    }

    /**
     * 构造证书支付的client
     * @return
     */
    private AlipayClient getAlipayClient() {
        //构造client
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest ();
        //设置网关地址
        certAlipayRequest.setServerUrl("https://openapi.alipay.com/gateway.do");
        //设置应用Id
        certAlipayRequest.setAppId(alipayProperties.getAppId());
        //设置应用私钥
        certAlipayRequest.setPrivateKey(alipayProperties.getPrivateKey());
        //设置请求格式，固定值json
        certAlipayRequest.setFormat("json");
        //设置字符集
        certAlipayRequest.setCharset("UTF-8");
        //设置签名类型
        certAlipayRequest.setSignType("RSA2");
        //设置应用公钥证书路径
        certAlipayRequest.setCertPath(alipayProperties.getAppCertPath());
        //设置支付宝公钥证书路径
        certAlipayRequest.setAlipayPublicCertPath(alipayProperties.getAlipayCertPath());
        //设置支付宝根证书路径
        certAlipayRequest.setRootCertPath(alipayProperties.getAlipayRootCertPath());
        try {
            AlipayClient client = new DefaultAlipayClient(certAlipayRequest);
            return client;
        } catch (Exception e) {
            log.error("PayController.getAlipayClient() error", e);
            return null;
        }
    }


}
